﻿// @brief
// @file Register.cs
// @date 2024年08月12日
// @author doublecat
// 
// Copyright (C) &lt;2024&gt;  &lt;doublecat&gt;
// 
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

using System.Globalization;
using System.Text;
using System.Xml.Serialization;

namespace doublecat.mcu.svd;

public class Register : IDimGroup, IRegisterGroup {
    private AccessEnum? _access;

    // 地址偏移
    private uint _addressOffset;

    private DataTypeEnum _dataType;

    private uint? _dim;

    private uint? _dimIncrement;

    private ModifyWriteValuesEnum? _modifiedWriteValues;

    private ProtectionEnum? _protection;

    private ReadActionEnum? _readAction;

    private uint? _resetMask;

    private uint? _resetValue;

    private uint? _size;

    [XmlElement("addressOffset")]
    public required string AddressOffset {
        get => $"0x{_addressOffset:X8}";
        set => _addressOffset = value.ToUInt()!.Value;
    }

    [XmlElement("alternateGroup")]
    public string? AlternateGroup { get; set; }

    [XmlElement("alternateRegister")]
    public string? AlternateRegister { get; set; }

    [XmlElement("dataType")]
    public string? DataType {
        get => _dataType.GetName();
        set => _dataType = value.ToDataTypeEnum()!.Value;
    }

    [XmlAttribute("derivedFrom")]
    public string? DerivedFrom { get; set; }

    [XmlElement("description")]
    public string? Description { get; set; }

    [XmlElement("displayName")]
    public string? DisplayName { get; set; }

    [XmlArray("fields")]
    [XmlArrayItem("field")]
    public List<Field> Fields { get; set; } = new List<Field>();

    [XmlElement("modifiedWriteValues")]
    public string? ModifiedWriteValues {
        get => _modifiedWriteValues.GetName();
        set => _modifiedWriteValues = value.ToModifyWriteValuesEnum();
    }

    [XmlElement("name")]
    public required string Name { get; set; }

    [XmlElement("readAction")]
    public string? ReadAction {
        get => _readAction.GetName();
        set => _readAction = value.ToReadActionEnum();
    }

    [XmlElement("writeConstraint")]
    public WriteConstraint? Constraint { get; set; }

    /// <summary>
    /// 寄存器所属的外设
    /// </summary>
    public required Peripheral Parent { get; set; }

    #region IDimGroup Members

    [XmlElement("dim")]
    public string? Dim {
        get => _dim.ToString();
        set => _dim = value.ToUInt();
    }

    [XmlElement("dimIncrement")]
    public string? DimIncrement {
        get => _dimIncrement?.ToString();
        set => _dimIncrement = value.ToUInt();
    }

    /// <summary>
    /// 列表索引：三种格式
    /// + 逗号分隔， 如：A,B,V
    /// + 连字号， 如： 0-9
    /// + 混合, 如：A,B, H-J,8-10,...
    /// </summary>
    [XmlElement("dimIndex")]
    public string? DimIndex { get; set; }

    [XmlElement("dimName")]
    public string? DimName { get; set; }

    [XmlElement("dimArrayIndex")]
    public DimArrayIndex? ArrayIndex { get; set; }

    #endregion

    #region IRegisterGroup Members

    /// <summary>
    /// 寄存器的默认位宽
    /// </summary>
    [XmlElement("size")]
    public string? Size {
        get => _size?.ToString();
        set => _size = value.ToUInt();
    }

    [XmlElement("access")]
    public string? Access {
        get => _access?.ToString();
        set => _access = value.ToAccessEnum();
    }

    [XmlElement("protection")]
    public string? Protection {
        get => _protection?.ToString();
        set => _protection = string.IsNullOrEmpty(value) ? null : value.ToProtectionEnum();
    }

    [XmlElement("resetValue")]
    public string? ResetValue {
        get => _resetValue.ToString();
        set => _resetValue = string.IsNullOrEmpty(value) ? null : value.ToUInt();
    }

    [XmlElement("resetMask")]
    public string? ResetMask {
        get => _resetMask.ToString();
        set => _resetMask = string.IsNullOrEmpty(value) ? null : value.ToUInt();
    }

    #endregion

    public uint GetAddressOffset() {
        return _addressOffset;
    }

    /// <summary>
    /// 获取寄存器位宽
    /// </summary>
    /// <returns></returns>
    public uint GetSize() {
        return _size ?? Parent.GetSize();
    }

    public uint GetDim() {
        return _dim ?? 0;
    }

    /// <summary>
    /// 对寄存器中的位域插补
    /// </summary>
    public List<Field> FieldsInterpolation() {
        List<Field> fields = Fields.OrderBy(field => field.BitOffset).ToList();
        Fields.Clear();
        uint offset = 0; // 地址偏移计数器
        foreach (Field field in fields) {
            // 如果当前位域偏移大于offset，则插入保留位
            if (field.BitOffset > offset) {
                uint bits = field.BitOffset - offset;
                Field f = new Field {
                    Name        = $"RESERVED_OFFSET_{offset}",
                    BitOffset   = offset,
                    BitWidth    = bits,
                    Description = $"Reserved bits:[bitOffset:{offset},bitWidth:{bits}]",
                    Parent      = this
                };
                Fields.Add(f);
                // 更新地址偏移计数器
                offset += bits;
            }
            // 更新地址偏移计数器
            offset += field.BitWidth;
            Fields.Add(field);
        }
        return Fields;
    }

    #region Overrides of Object

    public override string ToString() {
        StringBuilder sb = new StringBuilder();
        sb.Append("{")
          .Append($"'{Name}', 0x{AddressOffset:X8}")
          .Append("}");
        return sb.ToString();
    }

    #endregion

    public Field? FindField(string name) {
        return Fields.Find(f => f.Name == name);
    }

    /// <summary>
    /// 返回可作为变量名的字符串
    /// </summary>
    /// <remarks>
    /// 返回的字符串基于寄存器名称，并删除多余的字符
    /// </remarks>
    /// <returns>字符串</returns>
    public string GetVarName() {
        string name = GetName();
        // 检查名称中是否包含%s
        return name.Contains("%s") ? this.GenerateList(name) : name;
    }

    /// <summary>
    /// 返回有Name推导出来的的类型名称，去掉了占位符等多余的字符
    /// </summary>
    /// <returns></returns>
    /// <exception cref="NotImplementedException"></exception>
    public string GetTypeName() {
        string name = GetName();
        return !name.Contains("%s")
            ? name
            : name.Replace("%s", "").Replace("[", "").Replace("]", "");
    }

    /// <summary>
    /// 返回寄存器的名称，DimName > DisplayName > Name;
    /// </summary>
    /// <returns></returns>
    public string GetName() {
        return DimName ?? DisplayName ?? Name;
    }
}

public enum ModifyWriteValuesEnum {
    OneToClear,
    OneToSet,
    OneToToggle,
    ZeroToClear,
    ZeroToSet,
    ZeroToToggle,
    Clear,
    Set,
    Modify
}

public enum ReadActionEnum {
    Clear,
    Set,
    Modify,
    ModifyExternal
}

public enum DataTypeEnum {
    UInt8,
    UInt16,
    UInt32,
    UInt64,
    Int8,
    Int16,
    Int32,
    Int64,
    PUInt8,
    PUInt16,
    PUInt32,
    PUInt64,
    PInt8,
    PInt16,
    PInt32,
    PInt64
}

public static class RegisterHelper {
    public static ReadActionEnum? ToReadActionEnum(this string? s) {
        return s switch {
            "clear"          => ReadActionEnum.Clear,
            "set"            => ReadActionEnum.Set,
            "modify"         => ReadActionEnum.Modify,
            "modifyExternal" => ReadActionEnum.ModifyExternal,
            var _            => null
        };
    }

    public static string? GetName(this ReadActionEnum? p) {
        return p switch {
            ReadActionEnum.Clear          => "clear",
            ReadActionEnum.Modify         => "modify",
            ReadActionEnum.Set            => "set",
            ReadActionEnum.ModifyExternal => "modifyExternal",
            _                             => null
        };
    }

    /// <summary>
    /// 转换字符串为uint类型的数字
    /// </summary>
    /// <param name="str">一个数字字符串，可以是0x开头的十六进制字符串</param>
    /// <returns></returns>
    public static uint? ToUInt(this string? str) {
        return str != null && str.StartsWith("0x")
            ? uint.Parse(str[2..], NumberStyles.HexNumber)
            : uint.Parse(str!);
    }

    public static DataTypeEnum? ToDataTypeEnum(this string? str) {
        return str switch {
            "uint8_t"    => DataTypeEnum.UInt8,
            "uint16_t"   => DataTypeEnum.UInt16,
            "uint32_t"   => DataTypeEnum.UInt32,
            "uint64_t"   => DataTypeEnum.UInt64,
            "int8_t"     => DataTypeEnum.Int8,
            "int16_t"    => DataTypeEnum.Int16,
            "int32_t"    => DataTypeEnum.Int32,
            "int64_t"    => DataTypeEnum.Int64,
            "uint8_t *"  => DataTypeEnum.PUInt8,
            "uint16_t *" => DataTypeEnum.PUInt16,
            "uint32_t *" => DataTypeEnum.PUInt32,
            "uint64_t *" => DataTypeEnum.PUInt64,
            "int8_t *"   => DataTypeEnum.PInt8,
            "int16_t *"  => DataTypeEnum.PInt16,
            "int32_t *"  => DataTypeEnum.PInt32,
            "int64_t *"  => DataTypeEnum.PInt64,
            _            => null
        };
    }

    public static string GetName(this DataTypeEnum p) {
        return p switch {
            DataTypeEnum.UInt8   => "uint8_t",
            DataTypeEnum.UInt16  => "uint16_t",
            DataTypeEnum.UInt32  => "uint32_t",
            DataTypeEnum.UInt64  => "uint64_t",
            DataTypeEnum.Int8    => "int8_t",
            DataTypeEnum.Int16   => "int16_t",
            DataTypeEnum.Int32   => "int32_t",
            DataTypeEnum.Int64   => "int64_t",
            DataTypeEnum.PUInt8  => "uint8_t *",
            DataTypeEnum.PUInt16 => "uint16_t *",
            DataTypeEnum.PUInt32 => "uint32_t *",
            DataTypeEnum.PUInt64 => "uint64_t *",
            DataTypeEnum.PInt8   => "int8_t *",
            DataTypeEnum.PInt16  => "int16_t *",
            DataTypeEnum.PInt32  => "int32_t *",
            DataTypeEnum.PInt64  => "int64_t *",
            _                    => string.Empty
        };
    }

    public static ModifyWriteValuesEnum? ToModifyWriteValuesEnum(this string? str) {
        return str switch {
            "oneToClear"   => ModifyWriteValuesEnum.OneToClear,
            "oneToSet"     => ModifyWriteValuesEnum.OneToSet,
            "oneToToggle"  => ModifyWriteValuesEnum.OneToToggle,
            "zeroToClear"  => ModifyWriteValuesEnum.ZeroToClear,
            "zeroToSet"    => ModifyWriteValuesEnum.ZeroToSet,
            "zeroToToggle" => ModifyWriteValuesEnum.ZeroToToggle,
            "clear"        => ModifyWriteValuesEnum.Clear,
            "set"          => ModifyWriteValuesEnum.Set,
            "modify"       => ModifyWriteValuesEnum.Modify,
            _              => null
        };
    }

    public static string? GetName(this ModifyWriteValuesEnum? p) {
        return p switch {
            ModifyWriteValuesEnum.Clear        => "clear",
            ModifyWriteValuesEnum.Set          => "set",
            ModifyWriteValuesEnum.Modify       => "modify",
            ModifyWriteValuesEnum.OneToClear   => "oneToClear",
            ModifyWriteValuesEnum.OneToSet     => "oneToSet",
            ModifyWriteValuesEnum.OneToToggle  => "oneToToggle",
            ModifyWriteValuesEnum.ZeroToClear  => "zeroToClear",
            ModifyWriteValuesEnum.ZeroToSet    => "zeroToSet",
            ModifyWriteValuesEnum.ZeroToToggle => "zeroToToggle",
            _                                  => null
        };
    }
}
